package com.lx.utils;


import com.lx.utils.interfaces.ToBigDecimalFunction;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;
import java.util.function.*;
import java.util.stream.Collector;

/***
 * create by lanyxp on 2020/8/31 0031
 * <p>
 *    Collectors扩展版 针对BigDecimal 类型进行 添加 如果使用int long double
 *    使用{@link java.util.stream.Collectors#summarizingDouble(ToDoubleFunction)}
 *    使用{@link java.util.stream.Collectors#summarizingInt(ToIntFunction)}
 *    使用{@link java.util.stream.Collectors#summarizingLong(ToLongFunction)}
 *    {@link java.util.stream.Collectors#groupingBy(Function)}
 * </p>
 */
@NoArgsConstructor(access= AccessLevel.PRIVATE)
public final class ExpandCollectorsUtils {
    static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
    private static <I, R> Function<I, R> castingIdentity() {
        return i -> (R) i;
    }
    /****
     * <p>BigDecimal 类型进行累加计算</p>
     *
     * @param mapper a function extracting the property to be summed
     * @param <T> the type of the input elements
     * @return a {@code ExpandCollectorsUtils #summingBigDecimal } that produces the sum of a BigDecimal
     * {@link java.util.stream.Collectors}
     * <p>
     *     // Compute sum of salaries by department The result type is BigDecimal
     *     Map<Department, BigDecimal> totalByDept
     *         = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,ExpandCollectorsUtils.summingBigDecimal(Employee::getSalary)));
     *
     *     // Compute sum of salaries by department The result type is Integer
     *     Map<Department, Integer> totalByDept
     *         = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.summingInt(Employee::getSalary)));
     * </p>
     */
    public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(ToBigDecimalFunction<? super T> mapper) {
        return new CollectorImpl<>(() -> new BigDecimal[1], (a, t) -> {
            //创建一个计算用的容器
            if (a[0] == null) {
                a[0] = BigDecimal.ZERO;
            }
            //计算逻辑
            a[0] = a[0].add(mapper.applyAsBigDecimals(t));
        }, (a, b) -> {
            //BigDecimal.add 并将计算结果返回
            a[0] = a[0].add(b[0]);
            return a;
        }, a -> a[0], CH_NOID);
    }

    /**
     * Simple implementation class for {@code Collector}.
     *
     * @param <T> the type of elements to be collected
     * @param <R> the type of the result
     */
    @Data
    static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                      Function<A, R> finisher, Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }

        CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }


        @Override
        public Supplier<A> supplier() {
            return null;
        }

        @Override
        public BiConsumer<A, T> accumulator() {
            return null;
        }

        @Override
        public BinaryOperator<A> combiner() {
            return null;
        }

        @Override
        public Function<A, R> finisher() {
            return null;
        }

        @Override
        public Set<Characteristics> characteristics() {
            return null;
        }
    }
}
